<?php
declare(strict_types=1);

namespace Dreamcat\Http\EnhanceClient\Share;

use DreamCat\Array2Class\Array2ClassConverter;
use DreamCat\Array2Class\Array2ClassInterface;
use Dreamcat\Http\EnhanceClient\Exception\DecoderError;
use Dreamcat\Http\EnhanceClient\HttpClientInterface;
use Dreamcat\Http\EnhanceClient\Impl\GuzzleHttpClient;
use Dreamcat\Http\EnhanceClient\Impl\JsonDecoder;
use Dreamcat\Http\EnhanceClient\ResponseDecoderInterface;
use Zend\Diactoros\RequestFactory;
use Psr\Http\Client\ClientExceptionInterface;
use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Zend\Diactoros\StreamFactory;

/**
 * 为了避免无法继承写的trait
 * @author vijay
 */
trait HttpApiTrait
{
    /**
     * @var HttpClientInterface http客户端
     * @Autowire
     */
    private $httpClient;
    /**
     * @var RequestFactoryInterface 请求创建工厂
     * @Autowire
     */
    private $requestFactory;

    /**
     * @var StreamFactoryInterface 流创建工厂
     * @Autowire
     */
    private $streamFactory;

    /**
     * @var Array2ClassInterface 数组向类的转换器
     * @Autowire
     */
    private $array2class;

    /**
     * @var ResponseDecoderInterface 响应解码器
     * @Autowire
     */
    private $responseDecoder;

    /**
     * @param int $timeOutMs 默认的超时时间
     * @return static 对象本身
     */
    public function setTimeOutMs(?int $timeOutMs): self
    {
        $this->getHttpClient()->setTimeOutMs($timeOutMs);
        return $this;
    }

    /**
     * @return HttpClientInterface http客户端
     */
    public function getHttpClient(): HttpClientInterface
    {
        if (!$this->httpClient) {
            $this->httpClient = new GuzzleHttpClient();
        }
        return $this->httpClient;
    }

    /**
     * @param HttpClientInterface $httpClient http客户端
     * @return static 对象本身
     */
    public function setHttpClient(HttpClientInterface $httpClient): self
    {
        $this->httpClient = $httpClient;
        return $this;
    }

    /**
     * @return StreamFactoryInterface 流创建工厂
     */
    public function getStreamFactory(): StreamFactoryInterface
    {
        if (!$this->streamFactory) {
            $this->streamFactory = new StreamFactory();
        }
        return $this->streamFactory;
    }

    /**
     * @param StreamFactoryInterface $streamFactory 流创建工厂
     * @return static 对象本身
     */
    public function setStreamFactory(StreamFactoryInterface $streamFactory): self
    {
        $this->streamFactory = $streamFactory;
        return $this;
    }

    /**
     * @return RequestFactoryInterface 请求生成器
     */
    public function getRequestFactory(): RequestFactoryInterface
    {
        if (!$this->requestFactory) {
            $this->requestFactory = new RequestFactory();
        }
        return $this->requestFactory;
    }

    /**
     * @param RequestFactoryInterface $requestFactory 请求生成器
     * @return static 对象本身
     */
    public function setRequestFactory(RequestFactoryInterface $requestFactory): self
    {
        $this->requestFactory = $requestFactory;
        return $this;
    }

    /**
     * 发送请求并处理响应
     * @param RequestInterface $request 请求参数
     * @param string $outputClass 输出类名
     * @return mixed $outputClass类的对象
     * @throws ClientExceptionInterface
     */
    protected function send(RequestInterface $request, string $outputClass)
    {
        $response = $this->getHttpClient()->sendRequest($request);
        if ($response->getStatusCode() != 200) {
            throw new \RuntimeException("请求失败，http状态码{$response->getStatusCode()}", $response->getStatusCode());
        }
        return $this->formatOutput($response, $outputClass);
    }

    /**
     * 格式化返回数据
     * @param ResponseInterface $response 响应
     * @param string $className 输出类名
     * @return mixed $outputClass类的对象
     */
    protected function formatOutput(ResponseInterface $response, string $className)
    {
        $data = $this->getResponseDecoder()->decodeResponse($response->getBody()->getContents());
        return $this->getArray2class()->convert($data, $className);
    }

    /**
     * @return ResponseDecoderInterface 响应解码器
     */
    public function getResponseDecoder(): ResponseDecoderInterface
    {
        if (!$this->responseDecoder) {
            $this->responseDecoder = new JsonDecoder();
        }
        return $this->responseDecoder;
    }

    /**
     * @param ResponseDecoderInterface $responseDecoder 响应解码器
     * @return static 对象本身
     */
    public function setResponseDecoder(ResponseDecoderInterface $responseDecoder): self
    {
        $this->responseDecoder = $responseDecoder;
        return $this;
    }

    /**
     * @return Array2ClassInterface 数组向类的转换器
     */
    public function getArray2class(): Array2ClassInterface
    {
        if (!$this->array2class) {
            $this->array2class = new Array2ClassConverter();
            $this->array2class->setDefaultNotNull(false);
        }
        return $this->array2class;
    }

    /**
     * @param Array2ClassInterface $array2class 数组向类的转换器
     * @return static 对象本身
     */
    public function setArray2class(Array2ClassInterface $array2class): self
    {
        $this->array2class = $array2class;
        return $this;
    }

    /**
     * 格式化返回数据并生成数组
     * @param ResponseInterface $response 响应
     * @param string $className 输出类名
     * @return array $outputClass类的对象的数组
     */
    protected function formatOutput2List(ResponseInterface $response, string $className): array
    {
        $data = $this->getResponseDecoder()->decodeResponse($response->getBody()->getContents());
        if (!is_array($data)) {
            throw new DecoderError("得到的数据不是预期的数组");
        }
        $converter = $this->getArray2class();
        return array_map(function ($value) use ($converter, $className) {
            return $converter->convert($value, $className);
        }, $data);
    }

    /**
     * 发送请求并处理响应，将结果集返回为数组
     * @param RequestInterface $request 请求参数
     * @param string $outputClass 输出元素类名
     * @return array $outputClass类的对象的数组
     * @throws ClientExceptionInterface
     */
    protected function sendGetList(RequestInterface $request, string $outputClass): array
    {
        $response = $this->getHttpClient()->sendRequest($request);
        if ($response->getStatusCode() != 200) {
            throw new \RuntimeException("请求失败，http状态码{$response->getStatusCode()}", $response->getStatusCode());
        }
        return $this->formatOutput2List($response, $outputClass);
    }
}

# end of file
